
//:C07: {RunByHand}
// Using a queue and simulated multithreading
// to model a bank teller system.
#include <cstdlib>
#include <ctime>    
#include <iterator>
#include <list>
#include <iostream>
#include <queue>
using namespace std;

class Customer
{
    int serviceTime;
    public:
        Customer() : serviceTime(0) {}
        Customer(int tm) : serviceTime(tm) {}
        int getTime() { return serviceTime; }
        void setTime(int newtime) { serviceTime = newtime; }
        friend ostream&
        operator<<(ostream& os, const Customer& c)
        {
            return os << '[' << c.serviceTime << ']';
        }
};

class Teller
{
    queue<Customer>& customers;
    Customer current;
    enum { SLICE = 5 };
    int ttime;    // Time left in slice
    bool busy;    // Is teller serving a customer?
    public:
        Teller(queue<Customer>& cq)
        : customers(cq), ttime(0), busy(false) {}
        Teller& operator=(const Teller& rv)
        {
            customers = rv.customers;
            current = rv.current;
            ttime = rv.ttime;
            busy = rv.busy;
            return *this;
        }

        bool isBusy() { return busy; }
        void run(bool recursion = false)
        {
            if(!recursion)
            {
                ttime = SLICE;
            }

            int servtime = current.getTime();
            if(servtime > ttime)
            {
                servtime -= ttime;
                current.setTime(servtime);
                busy = true;    // Still working on current
                return;
            }

            if(servtime < ttime)
            {
                ttime -= servtime;
                if(!customers.empty())
                {
                    current = customers.front();
                    customers.pop();    // Remove it
                    busy = true;
                    run(true);    // Recurse
                }
                return;
            }

            if(servtime == ttime)
            {
                // Done with current, set to empty:
                current = Customer(0);
                busy = false;
                return;    // No more time in this slice
            }
        }
};

// Inherit to access protected implementation:
class CustomerQ  : public queue<Customer>
{
    public:
        friend ostream&
        operator<<(ostream& os, const CustomerQ& cd)
        {
            copy(cd.c.begin(), cd.c.end(),
                ostream_iterator<Customer>(os, ""));
            return os;    
        }
};

int main(void)
{
    CustomerQ customers;
    list<Teller> tellers;
    typedef list<Teller>::iterator TellIt;
    srand(time(0));    // Seed the random number generator
    clock_t ticks = clock();

    // Run simulation for at least 5 seconds:
    while (clock() < ticks + 5 * CLOCKS_PER_SEC)
    {
        // Add a random number of customers to the 
        // queue, with random service times:
        for (int i = 0; i < rand() % 5; i++)
        {
            customers.push(Customer(rand() % 15 + 1));
        }

        cout << '{' << tellers.size() << '}'
             << customers << endl;
        // Have the tellers service the queue:
        for (TellIt i = tellers.begin(); i != tellers.end(); i++)     
        {
            (*i).run();
        }
        cout << '{' << tellers.size() << '}'
             << customers << endl;

        // If line is too long, add another teller:
        if(customers.size() / tellers.size() > 2)     
        {
            tellers.push_back(Teller(customers));
        }

        // If line is short enough, remove a teller:
        if(tellers.size() > 1 && customers.size() / tellers.size() < 2)
        {
            for (TellIt i = tellers.begin(); i != tellers.end(); i++)
            {
                if(!(*i).isBusy())
                {
                    tellers.erase(i);
                    break;    // Out of for loop
                }
            }

        }
    }
}